package drds.binlog.prometheus;

import drds.binlog.instance.core.BinlogTask;
import drds.binlog.prometheus.impl.PrometheusClientInstanceProfiler;
import drds.binlog.server.netty.ClientInstanceProfiler;
import drds.binlog.spi.MetricsService;
import io.prometheus.client.exporter.HTTPServer;
import io.prometheus.client.hotspot.DefaultExports;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

import static drds.binlog.server.netty.CanalServerWithNettyProfiler.NOP;
import static drds.binlog.server.netty.CanalServerWithNettyProfiler.profiler;


public class PrometheusService implements MetricsService
{

    private static final Logger logger = LoggerFactory.getLogger(PrometheusService.class);
    private final InstanceExports instanceExports;
    private final ClientInstanceProfiler clientInstanceProfiler;
    private volatile boolean running = false;
    private int port;
    private HTTPServer httpServer;

    private PrometheusService()
    {
        this.instanceExports = InstanceExports.instance();
        this.clientInstanceProfiler = PrometheusClientInstanceProfiler.instance();
    }

    public static PrometheusService getInstance()
    {
        return SingletonHolder.SINGLETON;
    }

    @Override
    public void initialize()
    {
        try
        {
            logger.info("Start prometheus HTTPServer on port {}.", port);
            //TODO 2.Https?
            httpServer = new HTTPServer(port);
        } catch (IOException e)
        {
            logger.warn("Unable to start prometheus HTTPServer.", e);
            return;
        }
        try
        {
            // JVM exports
            DefaultExports.initialize();
            instanceExports.initialize();
            if (!clientInstanceProfiler.isRunning())
            {
                clientInstanceProfiler.start();
            }
            profiler().setInstanceProfiler(clientInstanceProfiler);
        } catch (Throwable t)
        {
            logger.warn("Unable to initialize httpServer exports.", t);
        }

        running = true;
    }

    @Override
    public void terminate()
    {
        running = false;
        try
        {
            instanceExports.terminate();
            if (clientInstanceProfiler.isRunning())
            {
                clientInstanceProfiler.stop();
            }
            profiler().setInstanceProfiler(NOP);
            if (httpServer != null)
            {
                httpServer.stop();
            }
        } catch (Throwable t)
        {
            logger.warn("Something happened while terminating.", t);
        }
    }

    @Override
    public boolean isRunning()
    {
        return running;
    }

    @Override
    public void register(BinlogTask binlogTask)
    {
        if (binlogTask.isRunning())
        {
            logger.warn("Cannot register metrics for destination {} that is running.", binlogTask.getDestination());
            return;
        }
        try
        {
            instanceExports.register(binlogTask);
        } catch (Throwable t)
        {
            logger.warn("Unable to register binlogTask exports for {}.", binlogTask.getDestination(), t);
        }
        logger.info("Register metrics for destination {}.", binlogTask.getDestination());
    }

    @Override
    public void unregister(BinlogTask binlogTask)
    {
        if (binlogTask.isRunning())
        {
            logger.warn("Try unregister metrics after destination {} is stopped.", binlogTask.getDestination());
        }
        try
        {
            instanceExports.unregister(binlogTask);
        } catch (Throwable t)
        {
            logger.warn("Unable to unregister binlogTask exports for {}.", binlogTask.getDestination(), t);
        }
        logger.info("Unregister metrics for destination {}.", binlogTask.getDestination());
    }

    @Override
    public void setServerPort(int port)
    {
        this.port = port;
    }

    private static class SingletonHolder
    {
        private static final PrometheusService SINGLETON = new PrometheusService();
    }

}
